'use strict';

module.exports = (function() {
  function processNode(node) {
    removeEmptyRulesets(node);
    mergeAdjacentWhitespace(node);
  }

  function removeEmptyRulesets(stylesheet) {
    stylesheet.forEach('ruleset', function(ruleset, i) {
      var block = ruleset.first('block');
      processNode(block);
      if (isEmptyBlock(block)) stylesheet.removeChild(i);
    });
  }

  /**
   * Removing ruleset nodes from tree may result in two adjacent whitespace
   * nodes which is not correct AST:
   * [space, ruleset, space] => [space, space]
   * To ensure correctness of further processing we should merge such nodes
   * into one:
   * [space, space] => [space]
   */
  function mergeAdjacentWhitespace(node) {
    var i = node.content.length - 1;
    while (i-- > 0) {
      if (node.get(i).is('space') &&
          node.get(i + 1) && node.get(i + 1).is('space')) {
        node.get(i).content += node.get(i + 1).content;
        node.removeChild(i + 1);
      }
    }
  }

  /**
   * Block is considered empty when it has nothing but spaces.
   */
  function isEmptyBlock(node) {
    if (!node.length) return true;

    return !node.content.some(function(node) {
      return !node.is('space');
    });
  }

  return {
    name: 'remove-empty-rulesets',

    runBefore: 'block-indent',

    syntax: ['css', 'less', 'sass', 'scss'],

    accepts: {
      boolean: [true]
    },

    /**
     * Remove rulesets with no declarations.
     *
     * @param {String} ast
     */
    process: function(ast) {
      processNode(ast);
    },

    detectDefault: true,

    /**
     * Detects the value of an option at the tree node.
     * This option is treated as `true` by default, but any trailing space
     * would invalidate it.
     *
     * @param {node} ast
     */
    detect: function(ast) {
      let detected = [];

      ast.traverse(function(node) {
        if (!node.is('atrulers') && !node.is('block')) return;

        if (node.length === 0 ||
            (node.length === 1 && node.first().is('space'))) {
          detected.push(false);
        }
      });

      return detected;
    }
  };
})();
